﻿using System;

namespace Encryption
{
	/*
		Hacked together class to encpryt and decrypt passwords for dbviewsharp
		Note: it is in no way safe. After all the algorithm will be posted 
		in Codeplex for all to find.

		The goal is to encode a password in such a way that a casual
		glance cannot make a guess at it.

		basic algorithm:
		shift characters in the word to be encrypted by applying the 
		keyword. 
		pad the encrypted string with random junk for every other
		character to the pad length.

	*/
	public class SoppyCrypt
	{
		public SoppyCrypt(String key, int encrytionLength)
		{
			this.InitialiseCrypt(key, encrytionLength);
		}

		public String Encrpyt(String source)
		{
			int ex = this.length - source.Length;
			ex = (ex > 0) ? ex : 0;
			Random rnd = new Random();
			char[] a = new char[source.Length + ex + 1];
			a[0] = (char)(48 + ex);
			int ptr = 1;
			int idx = 0;
			while (ptr < a.GetLength(0))
			{
				if (((ptr % 2) == 0) && (ptr < 2 * ex) || idx >= source.Length)
					a[ptr] = CHARACTER_MAP[rnd.Next(0,CHARACTER_MAP.Length-1)];
				else
					a[ptr] = this.Translate(source[idx++], Direction.encode);
				++ptr;
			}
			return new String(a);
		}

		public String Decrpyt(String source)
		{
			if (source.Length < this.length) return source;
			int ex = (int)(source[0] - 48);
			if (ex > this.length) return source;
			char[] a = new char[source.Length - ex -1];
			int ptr = 1;
			int idx = 0;
			while (ptr < source.Length)
			{
				if (((ptr % 2) == 0) && (ptr < 2 * ex) || idx >= a.GetLength(0))
					++ptr;
				else
					a[idx++] = this.Translate(source[ptr++], Direction.decode);
				;
			}
			return new String(a);
		}

		enum Direction {encode, decode};

		private char Translate(char p, Direction dir)
		{
			int idx = (dir == Direction.encode) ? this.outputString.IndexOf(p) : CHARACTER_MAP.IndexOf(p);
			if (idx == -1) return p;
			return (dir == Direction.encode) ? CHARACTER_MAP[idx] : this.outputString[idx];
		}

		private void InitialiseCrypt(String key, int encryptedStringLength)
		{
			String part1 = this.RemoveDupsFrom(key).ToUpper();
			String part2 = this.RemoveNonLettersFrom(part1).ToLower();
			this.length = encryptedStringLength;
			this.key = part1+part2+"123456789.";
			this.outputString = this.MakeTranslatedMap(CHARACTER_MAP, this.key);
			//Console.WriteLine("{0}\n{1}\n{2}\n", CHARACTER_MAP, m_Out, m_Key);
		}

		private string RemoveDupsFrom(string key)
		{
			String dupLess = "";
			foreach (char c in key)
			{
				if (CHARACTER_MAP.IndexOf(c) != -1)
					if (dupLess.IndexOf(c) == -1)
						dupLess += c;
			}
			return dupLess;
		}

		private string RemoveNonLettersFrom(string part)
		{
			String lettersOnly = "";
			foreach (char c in part)
			{
				if (c >= 'A' && c <= 'Z')
					lettersOnly += c;
			}
			return lettersOnly;
		}

		private string MakeTranslatedMap(string source, string key)
		{
			String target = key;
			foreach (char c in source)
			{
				if (target.IndexOf(c) == -1)
					target += c;
			}
			return target;
		}

		String  key;
		int     length;
		String  outputString;

//		const int PAD_LENGTH = 10;
		const String CHARACTER_MAP = "ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz)(*&^%$£!}{][~@:#';?></,|0123456789.";
	}
}
